import { Filter, MongoClient } from 'mongodb'
import { ProxyInterface } from '../etl'

export interface ProxyCRUD<T> {
    findAll(): Promise<T[]>
    findByParams(condition: any): Promise<T[]>
    insertOne(insertData: T): Promise<void>
    insertMany(insertDatas: T[]): Promise<void>
    findAndDelete(): Promise<T | null>
    deleteOne(id: string): Promise<void>
    updateOne(condition: any): Promise<void>
}



export class MongoDBClient<T extends { _id?: string }> implements ProxyCRUD<T> {
    constructor(
        protected client: MongoClient,
        protected db: string,
        protected collection: string
    ) { }

    async updateOne(condition: any): Promise<void> {
        const { _id } = condition
        if (!_id) {
            throw new Error('update method _id is undefined')
        }
        delete condition._id
        await this.col.updateOne({ _id }, { $set: condition })
    }

    async replaceOne(condition: any) {
        const { _id } = condition
        if (!_id) {
            throw new Error('replace method _id is undefined')
        }
        const res = await this.col.replaceOne({ _id }, condition, { upsert: true })
    }

    get col() {
        return this.client.db(this.db).collection<T>(this.collection)
    }

    async deleteOne(id: string): Promise<void> {
        await this.col.deleteOne({ _id: id } as any)
    }

    async findAndDelete(): Promise<T | null> {
        const { value } = await this.col.findOneAndDelete({})
        return value
    }

    async findAll(): Promise<T[]> {

        const data = await this.col.find({})
        const res = await data.toArray()
        return res
    }

    async findOne(condition: Filter<ProxyInterface>) {
        const res = await this.col.findOne()
        return res
    }

    async findByParams(condition: any): Promise<T[]> {
        const data = this.col.find(condition)
        const res = await data.toArray()
        return res
    }

    async insertOne(insertData: T) {
        const { insertedId } = await this.col.insertOne(insertData as any)
    }

    async insertMany(insertDatas: T[]) {
        const res = await this.col.insertMany(insertDatas as any)
    }

}


export async function createMogodb<T>(config: any, col: string) {
    const { db = "proxy", mongodb } = config
    const client: MongoClient = await mongodb.connect()
    return new MongoDBClient<T>(client, db, col)
}